import datetime

from database import db_session

from flask import session

from sqlalchemy import exc
from sqlalchemy import or_

from sqlalchemy.sql.expression import func

from models import *

import managers.seguridad_mgr

#Metodos

def listar_usuarios():
    """Devuelve la lista de usuarios del sistema, incluyendo unicamente 
    los que se encuentran en estado ACT
    @return items: retorna la lista de usuarios"""
    
    usuarios = db_session.query(Usuario).order_by(Usuario.id_usuario).all()
    items = []
    for fila_usuario in usuarios:
        if fila_usuario.estado == 'ACT':
            item = dict(id_usuario=fila_usuario.id_usuario, \
            nombre=fila_usuario.nombre, apellido=fila_usuario.apellido, \
            correo=fila_usuario.correo, estado=fila_usuario.estado)

            items.append(item)

    return items

def listar_usuarios_mgr():
    """Devuelve la lista de usuarios del sistema, incluyendo los 
    usuarios con estado INA
    @return items: retorna la lista de usuarios manager"""
    
    usuarios = db_session.query(Usuario).order_by(Usuario.id_usuario).all()
    items = []
    for fila_usuario in usuarios:
        item = dict(id_usuario=fila_usuario.id_usuario, \
        nombre=fila_usuario.nombre, apellido=fila_usuario.apellido, \
        correo=fila_usuario.correo, estado=fila_usuario.estado)

        items.append(item)

    return items

def buscar_usuarios(cadena, campos):
    """Realiza la busqueda de usuarios de acuerdo al filtro seleccionado
    (usuario, nombre, apellido, estado) y devuelve las conincidencias
    encontradas. (Unicamente los que se encuentran en estado ACT)
    @param cadena: Cadena de caracteres que se busca en los registros 
    @param campos: Filtro utilizado para la busqueda
    @return items: reti=orna una lista con las coincidencias encontradas 
                    en la busqueda"""
    
    items = []
    if campos == "todos":
        usuarios = db_session.query(Usuario).filter\
        (or_(func.lower(Usuario.id_usuario).contains(cadena), \
        func.lower(Usuario.nombre).contains(cadena), \
        func.lower(Usuario.apellido).contains(cadena), \
        func.lower(Usuario.correo).contains(cadena), \
        func.lower(Usuario.estado).contains(cadena))).all()
    if campos == "usuario":
        usuarios = db_session.query(Usuario).filter\
        (func.lower(Usuario.id_usuario).contains(cadena)).all()
    if campos == "nombre":
        usuarios = db_session.query(Usuario).filter\
        (func.lower(Usuario.nombre).contains(cadena)).all()
    if campos == "apellido":
        usuarios = db_session.query(Usuario).filter\
        (func.lower(Usuario.apellido).contains(cadena)).all()
    if campos == "correo":
        usuarios = db_session.query(Usuario).filter\
        (func.lower(Usuario.correo).contains(cadena)).all()
    if campos == "estado":
        usuarios = db_session.query(Usuario).filter\
        (func.lower(Usuario.estado).contains(cadena)).all()

    for fila_usuario in usuarios:
        if fila_usuario.estado == 'ACT':
            item = dict(id_usuario=fila_usuario.id_usuario, \
            nombre=fila_usuario.nombre, apellido=fila_usuario.apellido, \
            correo=fila_usuario.correo, estado=fila_usuario.estado, 
            tipo=fila_usuario.tipo)

            items.append(item)

    items = sorted(items, key=lambda elemento: elemento['id_usuario'])
    return items

def buscar_usuarios_mgr(cadena, campos):
    """Realiza la busqueda de usuarios de acuerdo al filtro seleccionado
    (usuario, nombre, apellido, estado) y devuelve las conincidencias
    encontradas. (Incluyendo los que se encuentran en estado INA)
    @param cadena: Cadena de caracteres que se busca en los registros 
    @param campos: Filtro utilizado para la busqueda
    @return items: reti=orna una lista con las coincidencias encontradas 
                    en la busqueda"""
    
    items = []
    if campos == "todos":
        usuarios = db_session.query(Usuario).filter\
        (or_(func.lower(Usuario.id_usuario).contains(cadena), \
        func.lower(Usuario.nombre).contains(cadena), \
        func.lower(Usuario.apellido).contains(cadena), \
        func.lower(Usuario.correo).contains(cadena), \
        func.lower(Usuario.estado).contains(cadena))).all()
    if campos == "usuario":
        usuarios = db_session.query(Usuario).filter\
        (func.lower(Usuario.id_usuario).contains(cadena)).all()
    if campos == "nombre":
        usuarios = db_session.query(Usuario).filter\
        (func.lower(Usuario.nombre).contains(cadena)).all()
    if campos == "apellido":
        usuarios = db_session.query(Usuario).filter\
        (func.lower(Usuario.apellido).contains(cadena)).all()
    if campos == "correo":
        usuarios = db_session.query(Usuario).filter\
        (func.lower(Usuario.correo).contains(cadena)).all()
    if campos == "estado":
        usuarios = db_session.query(Usuario).filter\
        (func.lower(Usuario.estado).contains(cadena)).all()

    for fila_usuario in usuarios:
        item = dict(id_usuario=fila_usuario.id_usuario, \
        nombre=fila_usuario.nombre, apellido=fila_usuario.apellido, \
        correo=fila_usuario.correo, estado=fila_usuario.estado)

        items.append(item)

    items = sorted(items, key=lambda elemento: elemento['id_usuario'])
    return items

def verificar_contrasena(id_usuario, pass_anterior, pass_nuevo, pass_confirmacion):
    """Realiza las validaciones necesarias para el cambio de contrasena.
    Una vez que se valida, se hace la modificacion en la base de datos
    @param id_usuario: El id del usuario que realiza la modificacion 
            de la contrasena
    @param pass_anterior: Contrasena anterior a la modificacion
    @param pass_nuevo: La nueva contrasena
    @param pass_confirmacion: La confirmacion de la nueva contrasena"""
    
    usuario = db_session.query(Usuario).filter_by\
    (id_usuario=id_usuario).one()

    if pass_anterior == '' or pass_nuevo == '' or pass_confirmacion == '':
        return 'Datos requeridos no ingresados'

    #if usuario.contrasena != pass_anterior:
    if not managers.seguridad_mgr.check_password\
    (usuario.contrasena, pass_anterior):
        return 'La contrasena anterior no es correcta'

    if pass_nuevo != pass_confirmacion:
        return 'La contrasena nueva no verifica'

    try:
        usuario.contrasena = managers.seguridad_mgr.set_password(pass_nuevo)
        usuario.fecha_modificacion = datetime.today()
        usuario.usuario_modificacion = usuario.id
        db_session.add(usuario)
        db_session.flush()
        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return 'No se pudo modificar la contrasena'

    return 'OK'

def verificar_parametros_usuario(id_usuario, nombre, apellido, \
contrasena, contrasena2):
    """Realiza la validaciones de los parametros ingresados para 
    el usuario
    @param id_usuario: id del usuario actual
    @param nombre: nombre del usuario
    @param apellido: apellido del usuario
    @param contrasena: contrasena del usuario
    @param contrasena2: verificacion de la contrasena"""
    
    sw = False

    if id_usuario == "":
        sw = True
    elif contrasena == "":
        sw = True
    elif contrasena2 == "":
        sw = True
    elif nombre == "":
        sw = True
    elif apellido == "":
        sw = True
    return sw

def insertar_usuario(id_usr_creador, id_usuario, nombre, apellido, \
correo, contrasena, tipo):
    """Realiza la insercion de un usuario en la tabla Usuarios
    @param id_usr_creador: id del usuario que carga elnuevo usuario
    @param id_usuario: id del usuario actual
    @param nombre: nombre del usuario
    @param apellido: apellido del usuario
    @param correo: correo del usuario
    @param contrasena: contrasena del usuario
    @param tipo:"""
    
    try:
        usr_test = db_session.query(Usuario). \
        filter_by(id_usuario=id_usuario).one()

        return 'El usuario ya existe'
    except exc.SQLAlchemyError:
        pass

    try:
        #Buscamos el pk del usuario creador
        usr = db_session.query(Usuario).filter_by(id_usuario=id_usr_creador).one()

        #Fecha actual
        fecha_actual = datetime.now()

        usuario = Usuario()
        usuario.id_usuario = id_usuario
        usuario.nombre = nombre
        usuario.apellido = apellido
        usuario.correo = correo
        usuario.contrasena = managers.seguridad_mgr.set_password(contrasena)
        usuario.tipo = tipo
        usuario.estado = 'ACT'
        usuario.fecha_creacion = fecha_actual
        usuario.usuario_creacion = usr.id
        usuario.fecha_modificacion = fecha_actual
        usuario.usuario_modificacion = usr.id
        db_session.add(usuario)
        db_session.flush()
        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message
        #return 'No se pudo insertar el usuario'

    return 'OK'

def validaciones_modificar_usuario(seleccionados):
    """Verifica que se seleccione un unico usuario, y que este se 
    encuentre en estado Activo
    @param seleccionados: Lista de usuarios seleccionados
    @return error: mensaje de error en caso de no cumplir con las validaciones"""
    
    error = None
    if len(seleccionados) == 0: #No selecciono ningun usuario
        error = "Debe seleccionar un usuario"
        return error
    if len(seleccionados) > 1: #Selecciono mas de un usuario
        error = "Debe seleccionar un unico usuario"
        return error

    id_usuario = seleccionados[0]
    usuario = db_session.query(Usuario).filter_by(id_usuario=\
    id_usuario).one()

    if usuario.estado == "INA":
        error = "Se requiere un cambio de estado a Activo"
        return error

    return error

def verificar_parametros_modificar_usuario(id_usuario, nombre, apellido):
    """Realiza la validaciones de los parametros ingresados para 
    la modificacion de usuario
    @param id_usuario: El id de usuario ingresado
    @param nombre: El nombre de usuario ingresado
    @param apellido: El apellido de usuario ingresado"""
    
    sw = False

    if id_usuario == "":
        sw = True
    elif nombre == "":
        sw = True
    elif apellido == "":
        sw = True
    return sw

def modificar_usuario(id_usr_modificador, id_usuario, nombre, apellido, \
correo):
    """Realiza la modificacion de los datos de un Usuario
    @param id_usuario_modificador: El id del usuario que realiza la 
    modificacion
    @param id_usuario: El id de usuario ingresado
    @param nombre: El nombre de usuario ingresado
    @param apellido: El apellido de usuario ingresado
    @param correo: El correo electronico ingresado"""
            
    #Si el nombre de usuario cambia, debe comprobarse que no exista
    #en la base de datos
    usr_seleccionado = session['id_usuario_seleccionado']
    if usr_seleccionado != id_usuario:
        try:
            usr_test = db_session.query(Usuario). \
            filter_by(id_usuario=id_usuario).one()

            return 'El usuario ya existe'
        except exc.SQLAlchemyError:
            pass

    try:
        #Buscamos al usuario modificador
        usr_modificador = db_session.query(Usuario).filter_by(id_usuario=\
        id_usr_modificador).one()

        #Buscamos al usuario a ser modificado
        usuario = db_session.query(Usuario).filter_by(id_usuario=\
        id_usuario).one()

        #Fecha actual
        fecha_actual = datetime.now()

        usuario.id_usuario = id_usuario
        usuario.nombre = nombre
        usuario.apellido = apellido
        usuario.correo = correo
        usuario.fecha_modificacion = fecha_actual
        usuario.usuario_modificacion = usr_modificador.id
        db_session.add(usuario)
        db_session.flush()
        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return 'OK'
 
def eliminar_usuario(seleccionados):
    """ Realiza la eliminacion de uno o mas usuarios.
    registra quien es el usuario que realiza la accion de eliminar
    y la fecha en que lo hace.
    @param seleccionados: trae la lista de usuarios seleccionados 
                          para ser eliminados
    @return: Exito o fracaso en la eliminacion del usuario"""
    
    id_usr_modificador = session['id_usuario']
    #Buscamos al usuario modificador
    usr_modificador = db_session.query(Usuario).filter_by(id_usuario=\
    id_usr_modificador).one()
    #Fecha actual
    fecha_actual = datetime.now()
    
    try:
        for elemento in seleccionados:
            usuario = db_session.query(Usuario).filter_by(id_usuario=\
            elemento).one()
            usuario.estado = "INA"
            usuario.usuario_modificacion = usr_modificador.id
            usuario.fecha_modificacion = fecha_actual
            db_session.add(usuario)
            db_session.flush()
            db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message
    return 'OK'

def cambiar_estado_usuario(seleccionados):
    """ Realiza el cambio de estado de uno o mas usuarios
    registra quien es el usuario que realiza la accion de eliminar
    y la fecha en que lo hace.
    @param seleccionados: trae la lista de usuarios seleccionados 
                          para ser cambiadosde estado
    @return: Exito o fracaso en el cambio de estado de usuario"""
    
    id_usr_modificador = session['id_usuario']
    #Buscamos al usuario modificador
    usr_modificador = db_session.query(Usuario).filter_by(id_usuario=\
    id_usr_modificador).one()
    #Fecha actual
    fecha_actual = datetime.now()

    try:
        for elemento in seleccionados:
            usuario = db_session.query(Usuario).filter_by(id_usuario=\
            elemento).one()
            usuario.estado = "ACT"
            usuario.usuario_modificacion = usr_modificador.id
            usuario.fecha_modificacion = fecha_actual
            db_session.add(usuario)
            db_session.flush()
            db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message
    return 'OK'   

def validaciones_activar_usuario(seleccionados):
    """Verifica que el dato haya sido seleccionado y que el mismo 
    corresponda a un solo usuario. Tambien corrobora que el estado del 
    usuario este Inactivo
    @param seleccionados: trae la lista de usuarios seleccionados 
                          para ser cambiados de estado
    @return error: retorna un mensaje de error en caso de no ser seleccionado 
            al menos un usuario. asi tambien en caso de que el usuario 
            este en estado Inactivo"""
            
    error = None
    if len(seleccionados) == 0: #No selecciono ningun usuario
        error = "Debe seleccionar al menos usuario"
        return error
    for elemento in seleccionados:    
        id_usuario = elemento
        usuario = db_session.query(Usuario).filter_by(id_usuario=\
        id_usuario).one()
        if usuario.estado == "ACT":
            error = "Se requiere un cambio de estado a Inactivo"
            return error

    return error

def validaciones_eliminar_usuario(seleccionados):
    """Verifica que el dato haya sido seleccionado y que el mismo 
    corresponda a un solo usuario y ademas que el usuario a ser 
    eliminado no sea el mismo. Tambien corrobora que el estado del 
    usuario este Activo
    @param seleccionados: trae la lista de usuarios seleccionados 
                          para ser eliminados
    @return error: retorna un mensaje de error en caso de no ser seleccionado 
            al menos un usuario. Tambien verifica que el usuario que 
            halla iniciado la secion no se pueda eliminar. Asi tambien 
            en caso de que el usuario este en estado activo"""
            
    error = None
    if len(seleccionados) == 0: #No selecciono ningun usuario
        error = "Debe seleccionar al menos usuario"
        return error
   
    for elemento in seleccionados:
        if session["id_usuario"] == elemento:
            error = "No puede eliminarse a si mismo"
            return error
            
    for elemento in seleccionados:
        id_usuario = elemento
        usuario = db_session.query(Usuario).filter_by(id_usuario=\
        id_usuario).one()
        if usuario.estado == "INA":
            error = "Se requiere un cambio de estado a Activo"
            return error

    return error
